package com.container.core;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.Servlet;
import javax.servlet.ServletException;
import java.io.IOException;
import java.util.*;

public class MyUrlPatternMapper {
    private final Logger log = LoggerFactory.getLogger(getClass());
    private AllMappedServlets allMappedServlets;
    private String contextPath;

    //在MyServletContext中调用
    public MyUrlPatternMapper(String contextPath) {
        this.allMappedServlets = new AllMappedServlets();
        this.contextPath = contextPath;
    }

    //在MyServletContext的addMapping方法中调用
    public void addServlet(String url, Servlet servlet, String servletName) throws ServletException {
    	String pattern;
        if (url.endsWith("/*")) {
            //路径匹配
            pattern = url.substring(0, url.length()-1);
            for (MappedServlet ms : allMappedServlets.wildcardServlets) {
                if (ms.pattern.equals(pattern)) {
                    throw new ServletException("URL Pattern('" + url + "') already exists!");
                }
            }
            MappedServlet newServlet = new MappedServlet(pattern, servlet, servletName);
            allMappedServlets.wildcardServlets.add(newServlet);
            allMappedServlets.wildcardServlets.sort((o1, o2) -> o2.pattern.compareTo(o1.pattern));
            log.debug("Curretn Wildcard URL Pattern List = " + Arrays.toString(allMappedServlets.wildcardServlets.toArray()));

        } else if (url.startsWith("*.")) {
            //扩展名匹配
            pattern = url.substring(2);
            if (allMappedServlets.extensionServlets.get(pattern) != null) {
                throw new ServletException("URL Pattern('" + url + "') already exists!");
            }
            MappedServlet newServlet = new MappedServlet(pattern, servlet, servletName);
            allMappedServlets.extensionServlets.put(pattern, newServlet);
            log.debug("Curretn Extension URL Pattern List = " + Arrays.toString(allMappedServlets.extensionServlets.keySet().toArray()));

        } else if (url.equals("/")) {
            //默认匹配
            if (allMappedServlets.defaultServlet != null) {
                throw new ServletException("URL Pattern('" + url + "') already exists!");
            }
            allMappedServlets.defaultServlet = new MappedServlet("", servlet, servletName);

        } else {
            //精确匹配
            if (url.length() == 0) {
                pattern = "/";
            } else {
                pattern = url;
            }
            if (allMappedServlets.exactServlets.get(pattern) != null) {
                throw new ServletException("URL Pattern('" + url + "') already exists!");
            }
            MappedServlet newServlet = new MappedServlet(pattern, servlet, servletName);
            allMappedServlets.exactServlets.put(pattern, newServlet);
            log.debug("Curretn Exact URL Pattern List = " + Arrays.toString(allMappedServlets.exactServlets.keySet().toArray()));
        }
    }

    public void removeServlet(String url) {
    	String pattern;
        if (url.endsWith("/*")) {
            //路径匹配
            pattern = url.substring(0, url.length()-2);
            allMappedServlets.wildcardServlets.removeIf(mappedServlet -> mappedServlet.pattern.equals(pattern));

        } else if (url.startsWith("*.")) {
            //扩展名匹配
            pattern = url.substring(2);
            allMappedServlets.extensionServlets.remove(pattern);

        } else if (url.equals("/")) {
            //默认匹配
            allMappedServlets.defaultServlet = null;

        } else {
            //精确匹配
            if (url.length() == 0) {
                pattern = "/";
            } else {
                pattern = url;
            }
            allMappedServlets.exactServlets.remove(pattern);
        }
    }

    //在MyServletContext的getRequestDispatcher方法中调用，注意这里传入的url是绝对路径
    public String getServletName(String url) {
        MappingData mappingData = new MappingData();
        try {
            findPath(url, mappingData);
        } catch (IOException e) {
            log.error("Throwing exception when getting Servlet Name by request URI, maybe cause by lacking of buffer size.", e);
        }
        return mappingData.servletName;
    }

    private void findPath(String originalPath, MappingData mappingData) throws IOException {
        if (!originalPath.startsWith(contextPath)) return;

        boolean onlyContextPath = originalPath.equals(contextPath) || originalPath.equals(contextPath + "/");//判断路径中是否包含Servlet信息
        String path = onlyContextPath ? "/" : originalPath.substring(contextPath.length());
        int queryInx = path.indexOf('?');//检查有没有查询字符串，有的话去掉
        if(queryInx > -1) path = path.substring(0, queryInx);

        //精确匹配
        exactMapping(allMappedServlets.exactServlets, path, mappingData);

        //路径匹配
        if (mappingData.servlet == null) {
            wildcardMapping(allMappedServlets.wildcardServlets, path, mappingData);
        }

        //servlet路径为空时，使用默认匹配
        if (onlyContextPath && mappingData.servlet == null) {
        	if (allMappedServlets.defaultServlet != null) {
            	mappingData.servlet = allMappedServlets.defaultServlet.mServlet;
            	mappingData.servletName = allMappedServlets.defaultServlet.servletName;
            	return;
        	}
        }

        //扩展名匹配
        if (mappingData.servlet == null) {
            extensionMapping(allMappedServlets.extensionServlets, path, mappingData);
        }

        //默认匹配
        if (mappingData.servlet == null) {
            if (allMappedServlets.defaultServlet != null) {
                mappingData.servlet = allMappedServlets.defaultServlet.mServlet;
                mappingData.servletName = allMappedServlets.defaultServlet.servletName;
            }
        }
    }

    private void exactMapping(Map<String, MappedServlet> servlets, String path, MappingData mappingData) {
        MappedServlet servlet = servlets.get(path);
        if (servlet != null) {
            mappingData.servlet = servlet.mServlet;
            mappingData.servletName = servlet.servletName;
        }
    }

    private void wildcardMapping(List<MappedServlet> servlets, String path, MappingData mappingData) {
        if (!path.endsWith("/")) {
            path = path + "/";
        }

        MappedServlet servlet = null;
        for (MappedServlet ms : servlets) {
            if (path.startsWith(ms.pattern)) {
                servlet = ms;
                break;
            }
        }

        if (servlet != null) {
            mappingData.servlet = servlet.mServlet;
            mappingData.servletName = servlet.servletName;
        }
    }

    private void extensionMapping(Map<String, MappedServlet> servlets, String path, MappingData mappingData) {
        int idx = path.lastIndexOf('.');
        path = path.substring(idx + 1);
        MappedServlet servlet = servlets.get(path);
        if (servlet != null) {
            mappingData.servlet = servlet.mServlet;
            mappingData.servletName = servlet.servletName;
        }
    }

    private class MappingData {
	    Servlet servlet;
	    String servletName;

	    public void recycle() {
	        servlet = null;
	        servletName = null;
	    }
	}

    private class AllMappedServlets {
        MappedServlet defaultServlet = null;//默认
        List<MappedServlet> wildcardServlets = new LinkedList<>();//路径
        Map<String, MappedServlet> extensionServlets = new HashMap<>();//扩展名
        Map<String, MappedServlet> exactServlets = new HashMap<>();//精确  
    }

    private class MappedServlet {
    	final String pattern;
        final Servlet mServlet;
        String servletName;

        MappedServlet(String pattern, Servlet servlet) {
            this.pattern = pattern;
            this.mServlet = servlet;
        }

        MappedServlet(String pattern, Servlet servlet, String servletName) {
            this.pattern = pattern;
            this.mServlet = servlet;
            this.servletName = servletName;
        }

        @Override
        public String toString() {
            return pattern;
        }
    }
}